{
  Copyright 2012 Sergey Ostanin

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
}

unit ModifierListEditor;

interface

uses
  Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
  ExtCtrls, StdCtrls, ActnList, Menus, TestCore, VisualUtils, CommCtrl,
  Modifiers, Math, ModifierEditors, TestUiUtils, MiscUtils;

type
  TModifierListEditorForm = class(TForm)
    actAdd: TAction;
    actDelete: TAction;
    actMoveDown: TAction;
    actMoveUp: TAction;
    alModifiers: TActionList;
    btnOk: TButton;
    btnCancel: TButton;
    ilModifiers: TImageList;
    lvModifiers: TListView;
    miMoveDown: TMenuItem;
    miMoveUp: TMenuItem;
    miDelete: TMenuItem;
    pnlModifier: TPanel;
    pnlBottom: TPanel;
    pmAdd: TPopupMenu;
    pmModifiers: TPopupMenu;
    tbrModifiers: TToolBar;
    tbAdd: TToolButton;
    tbDelete: TToolButton;
    tbSeparator1: TToolButton;
    tbMoveUp: TToolButton;
    tbMoveDown: TToolButton;
    procedure actDeleteExecute(Sender: TObject);
    procedure actDeleteUpdate(Sender: TObject);
    procedure actMoveDownExecute(Sender: TObject);
    procedure actMoveDownUpdate(Sender: TObject);
    procedure actMoveUpExecute(Sender: TObject);
    procedure actMoveUpUpdate(Sender: TObject);
    procedure btnOkClick(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure lvModifiersSelectItem(Sender: TObject; Item: TListItem;
      Selected: Boolean);
    procedure pmAddPopup(Sender: TObject);
  private
    FList: TModifierList;
    FQuestionKind: TKind;
    FModifierEditor: TFrame;
    procedure Prepare;
    procedure AddClick(Sender: TObject);
    procedure ReleaseModifierEditor;
    procedure Commit;
    function SelectedModifier: TModifier;
    { private declarations }
  public
    destructor Destroy; override;
    class function OpenModal(List: TModifierList; QuestionKind: TKind = 0): Boolean;
    { public declarations }
  end;

implementation

{$R *.lfm}
{$R *.rc}

{ TModifierListEditorForm }

procedure TModifierListEditorForm.actDeleteExecute(Sender: TObject);
var
  Item, SelectItem: TListItem;
  Index: Integer;
begin
  ReleaseModifierEditor;

  Item := lvModifiers.Selected;
  Index := Item.Index;

  FList.Delete(Index);
  Item.Delete;

  if FList.Count > 0 then
  begin
    SelectItem := lvModifiers.Items[Min(Index, FList.Count-1)];
    SelectItem.Selected := TRUE;
    lvModifiers.ItemFocused := SelectItem;
  end;
end;

procedure TModifierListEditorForm.actDeleteUpdate(Sender: TObject);
begin
  (Sender as TAction).Enabled := ListViewHasSelection(lvModifiers);
end;

procedure TModifierListEditorForm.actMoveDownExecute(Sender: TObject);
var
  Index: Integer;
begin
  Index := lvModifiers.Selected.Index;
  FList.Exchange(Index, Index+1);
  lvModifiers.Items.Exchange(Index, Index+1);
end;

procedure TModifierListEditorForm.actMoveDownUpdate(Sender: TObject);
var
  Sel: TListItem;
begin
  Sel := lvModifiers.Selected;
  (Sender as TAction).Enabled := (Sel <> nil) and (Sel.Index < FList.Count-1);
end;

procedure TModifierListEditorForm.actMoveUpExecute(Sender: TObject);
var
  Index: Integer;
begin
  Index := lvModifiers.Selected.Index;
  FList.Exchange(Index, Index-1);
  lvModifiers.Items.Exchange(Index, Index-1);
end;

procedure TModifierListEditorForm.actMoveUpUpdate(Sender: TObject);
var
  Sel: TListItem;
begin
  Sel := lvModifiers.Selected;
  (Sender as TAction).Enabled := (Sel <> nil) and (Sel.Index > 0);
end;

procedure TModifierListEditorForm.btnOkClick(Sender: TObject);
begin
  Commit;
  ModalResult := mrOk;
end;

procedure TModifierListEditorForm.FormShow(Sender: TObject);
begin
  if FModifierEditor <> nil then
    SelectFirstChild(FModifierEditor);
end;

procedure TModifierListEditorForm.lvModifiersSelectItem(Sender: TObject;
  Item: TListItem; Selected: Boolean);
var
  m: TModifier;
  EditorClass: TFrameClass;
begin
  if not Selected then
    Commit;

  ReleaseModifierEditor;

  m := SelectedModifier;
  if m <> nil then
  begin
    EditorClass := ModifierEditorRegistry.TryFind(TModifierClass(m.ClassType));
    if EditorClass <> nil then
    begin
      FModifierEditor := EditorClass.Create(nil);
      try
        FModifierEditor.Align := alClient;
        FModifierEditor.Parent := pnlModifier;
        (FModifierEditor as IModifierEditor).SetUp(m);
      except
        FreeAndNil(FModifierEditor);
        raise;
      end;
    end;
  end;
end;

procedure TModifierListEditorForm.pmAddPopup(Sender: TObject);
var
  mc: TModifierClass;
  Item: TMenuItem;
begin
  pmAdd.Items.Clear;
  for mc in ModifierRegistry.List do
  begin
    if not mc.IsLegacy and ((FQuestionKind = 0) or mc.IsApplicableTo(FQuestionKind)) then
    begin
      Item := TMenuItem.Create(nil);
      try
        Item.Caption := mc.GetTitle;
        {$HINTS OFF}
        Item.Tag := PtrInt(mc);
        {$HINTS ON}
        if FList.ContainsInstanceOf(mc) then
          Item.Enabled := FALSE
        else
          Item.OnClick := AddClick;
        pmAdd.Items.Add(Item);
      except
        Item.Free;
        raise;
      end;
    end;
  end;
end;

procedure TModifierListEditorForm.Prepare;
var
  i: Integer;
begin
  HandleNeeded;

  LoadActionImages(alModifiers);
  AppendMenuShortcutsToActionHints(pmModifiers);

  MakeListViewDoubleBuffered(lvModifiers);
  lvModifiers.Height := GetPreferredListViewHeight(lvModifiers, ModifierRegistry.Count);
  lvModifiers.AutoWidthLastColumn := TRUE;

  StretchToWidest([btnOk, btnCancel]);

  for i := 0 to FList.Count-1 do
    lvModifiers.Items.Add.Caption := FList[i].GetTitle;
  if FList.Count > 0 then
  begin
    lvModifiers.Items[0].Selected := TRUE;
    lvModifiers.ItemFocused := lvModifiers.Items[0];
  end;
end;

procedure TModifierListEditorForm.AddClick(Sender: TObject);
var
  mc: TModifierClass;
  m: TModifier;
  Item: TListItem;
begin
  {$HINTS OFF}
  mc := TModifierClass((Sender as TMenuItem).Tag);
  {$HINTS ON}
  m := mc.Create;
  FList.Add(m);

  Item := lvModifiers.Items.Add;
  Item.Caption := mc.GetTitle;
  Item.Selected := TRUE;
  lvModifiers.ItemFocused := Item;

  if FModifierEditor <> nil then
    SelectFirstChild(FModifierEditor);
end;

procedure TModifierListEditorForm.ReleaseModifierEditor;
begin
  if FModifierEditor <> nil then
  begin
    FModifierEditor.Parent := nil;
    Application.ReleaseComponent(FModifierEditor);
    FModifierEditor := nil;
  end;
end;

procedure TModifierListEditorForm.Commit;
begin
  if FModifierEditor <> nil then
    (FModifierEditor as IModifierEditor).Commit;
end;

destructor TModifierListEditorForm.Destroy;
begin
  FreeAndNil(FModifierEditor);
  inherited;
end;

class function TModifierListEditorForm.OpenModal(List: TModifierList;
  QuestionKind: TKind = 0): Boolean;
var
  Form: TModifierListEditorForm;
begin
  Form := TModifierListEditorForm.Create(nil);
  try
    Form.Width := ScalePixels(825);
    Form.Height := ScalePixels(550);
    RestoreFormPosition(Form);

    Form.FList := List;
    Form.FQuestionKind := QuestionKind;
    Form.Prepare;

    Result := ShowModalForm(Form) = mrOk;
  finally
    Form.Free;
  end;
end;

function TModifierListEditorForm.SelectedModifier: TModifier;
var
  Sel: TListItem;
begin
  Sel := lvModifiers.Selected;
  if Sel = nil then
    Result := nil
  else
    Result := FList[Sel.Index];
end;

end.

